﻿<!DOCTYPE html>
<html lang="zh-CN">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>期货AI量化研究报告展示</title>
    <!-- 引入 ECharts 库 -->
    <script src="https://cdn.jsdelivr.net/npm/echarts@5.6.0/dist/echarts.min.js"></script>
    <!-- 引入 Marked.js 用于解析 Markdown -->
    <script src="js/marked.min.js"></script>
    <!-- 引入 FontAwesome 用于图标 -->
    <link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/@fortawesome/fontawesome-free@6.4.2/css/all.min.css">

    <style>
        /* CSS样式部分保持不变，这里省略以节约篇幅。请使用上一版中的CSS代码。 */
        /* --- 1. 全局与变量定义 --- */
        :root {
            --bg-color: #f4f7f9;
            --sidebar-bg: #ffffff;
            --content-bg: #ffffff;
            --text-color: #333333;
            --text-color-secondary: #666666;
            --border-color: #e4e7ed;
            --primary-color: #409eff;
            --primary-color-light: #ecf5ff;
            --header-height: 60px;
            --sidebar-width: 260px;
            --toc-width: 220px;
        }

        .dark-theme {
            --bg-color: #141414;
            --sidebar-bg: #1d1d1d;
            --content-bg: #1d1d1d;
            --text-color: #e0e0e0;
            --text-color-secondary: #a0a0a0;
            --border-color: #3a3a3a;
            --primary-color: #2a7fdb;
            --primary-color-light: #28334a;
        }

        * {
            box-sizing: border-box;
            margin: 0;
            padding: 0;
        }

        body {
            font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Arial, "Noto Sans", sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol", "Noto Color Emoji";
            background-color: var(--bg-color);
            color: var(--text-color);
            transition: background-color 0.3s, color 0.3s;
            overflow-x: hidden;
        }

        /* --- 2. 页面布局 --- */
        .app-layout {
            display: grid;
            grid-template-columns: var(--sidebar-width) 1fr var(--toc-width);
            grid-template-rows: var(--header-height) 1fr;
            grid-template-areas:
                "header header header"
                "sidebar content toc";
            height: 100vh;
        }

        .app-header {
            grid-area: header;
        }

        .app-sidebar {
            grid-area: sidebar;
        }

        .main-content {
            grid-area: content;
        }

        .table-of-contents {
            grid-area: toc;
        }

        /* --- 3. 组件样式 --- */
        /* 页头 */
        .app-header {
            display: flex;
            align-items: center;
            justify-content: space-between;
            padding: 0 24px;
            border-bottom: 1px solid var(--border-color);
            background-color: var(--sidebar-bg);
            transition: background-color 0.3s, border-color 0.3s;
            z-index: 100;
        }

            .app-header .logo {
                /* --- 新增/修改的属性 --- */
                display: flex; /* 1. 启用Flexbox布局 */
                align-items: center; /* 2. 让所有子元素在交叉轴（垂直方向）上居中对齐 */
                gap: 15px; /* 3. (推荐) 使用gap替代span的margin-left，更灵活 */

                font-size: 20px;
                font-weight: 600;
            }

            .app-header .theme-switcher {
                font-size: 20px;
                cursor: pointer;
                color: var(--text-color-secondary);
            }

        /* 侧边栏 */
        .app-sidebar {
            background-color: var(--sidebar-bg);
            padding: 20px 0;
            border-right: 1px solid var(--border-color);
            overflow-y: auto;
            transition: background-color 0.3s, border-color 0.3s;
        }

        .menu-group .menu-title {
            padding: 12px 20px;
            font-size: 16px;
            font-weight: 500;
            cursor: pointer;
            display: flex;
            justify-content: space-between;
            align-items: center;
        }

            .menu-group .menu-title:hover {
                background-color: var(--bg-color);
            }

            .menu-group .menu-title .icon {
                transition: transform 0.3s;
            }

        .menu-group.open .menu-title .icon {
            transform: rotate(90deg);
        }

        .menu-items {
            max-height: 0;
            overflow: hidden;
            transition: max-height 0.3s ease-in-out;
        }

        .menu-group.open .menu-items {
            max-height: 500px; /* 一个足够大的值 */
        }

        .menu-items .item {
            display: block;
            padding: 10px 20px 10px 40px;
            font-size: 14px;
            color: var(--text-color-secondary);
            text-decoration: none;
        }

            .menu-items .item:hover {
                color: var(--primary-color);
            }

            .menu-items .item.active {
                background-color: var(--primary-color-light);
                color: var(--primary-color);
                font-weight: 500;
            }

        /* 主内容区 */
        .main-content {
            overflow-y: auto;
            padding: 32px;
            background-color: var(--bg-color);
        }

        #report-container {
            max-width: 800px;
            margin: 0 auto;
            background-color: var(--content-bg);
            padding: 40px;
            border-radius: 8px;
            box-shadow: 0 4px 12px rgba(0,0,0,0.05);
            transition: background-color 0.3s;
        }
        /* Markdown 渲染样式 */
        #report-content h1, #report-content h2, #report-content h3 {
            border-bottom: 1px solid var(--border-color);
            padding-bottom: 0.3em;
            margin-top: 24px;
            margin-bottom: 16px;
        }

        #report-content h1 {
            font-size: 2em;
        }

        #report-content h2 {
            font-size: 1.5em;
        }

        #report-content h3 {
            font-size: 1.25em;
        }

        #report-content p {
            line-height: 1.8;
            margin-bottom: 1em;
        }

        #report-content ul, #report-content ol {
            padding-left: 2em;
        }

        #report-content li {
            margin-bottom: 0.5em;
        }

        #report-content code {
            background-color: var(--bg-color);
            padding: .2em .4em;
            margin: 0;
            font-size: 85%;
            border-radius: 3px;
        }

        #report-content blockquote {
            padding: 0 1em;
            color: var(--text-color-secondary);
            border-left: 0.25em solid var(--border-color);
            margin-left: 0;
        }
        /* ECharts 容器 */
        .echart-placeholder {
            width: 100%;
            height: 450px;
            margin: 2rem 0;
            border-radius: 8px;
            border: 1px solid var(--border-color);
        }

        /* 目录 */
        .table-of-contents {
            padding: 20px;
            overflow-y: auto;
        }

        .toc-title {
            font-size: 16px;
            font-weight: 600;
            margin-bottom: 16px;
        }

        .toc-list {
            list-style: none;
        }

            .toc-list a {
                display: block;
                padding: 8px 0;
                color: var(--text-color-secondary);
                text-decoration: none;
                font-size: 14px;
                border-left: 2px solid transparent;
                padding-left: 12px;
                transition: all 0.2s;
            }

                .toc-list a:hover {
                    color: var(--primary-color);
                }

                .toc-list a.active {
                    color: var(--primary-color);
                    border-left-color: var(--primary-color);
                    font-weight: 500;
                    transform: scale(1.02);
                }

            .toc-list .toc-h3 {
                padding-left: 24px;
            }


        /* --- 4. 响应式设计 --- */
        .mobile-menu-toggle {
            display: none;
            font-size: 20px;
            cursor: pointer;
        }

        @media (max-width: 1200px) {
            .app-layout {
                grid-template-columns: var(--sidebar-width) 1fr;
                grid-template-areas:
                    "header header"
                    "sidebar content";
            }

            .table-of-contents {
                display: none;
            }
            /* 在中等屏幕隐藏目录 */
        }

        @media (max-width: 768px) {
            .app-layout {
                grid-template-columns: 1fr;
                grid-template-areas:
                    "header"
                    "content";
            }

            .mobile-menu-toggle {
                display: block;
            }

            .app-sidebar {
                position: fixed;
                left: 0;
                top: 0;
                height: 100%;
                transform: translateX(-100%);
                transition: transform 0.3s ease-in-out;
                z-index: 200;
                width: 280px;
                border-right: 1px solid var(--border-color);
            }

                .app-sidebar.open {
                    transform: translateX(0);
                    box-shadow: 0 0 20px rgba(0,0,0,0.2);
                }

            .overlay {
                position: fixed;
                top: 0;
                left: 0;
                width: 100%;
                height: 100%;
                background-color: rgba(0,0,0,0.4);
                z-index: 199;
                opacity: 0;
                pointer-events: none;
                transition: opacity 0.3s;
            }

                .overlay.active {
                    opacity: 1;
                    pointer-events: auto;
                }

            #report-container {
                padding: 20px;
            }
        }
    </style>
</head>
<body class="dark-theme">

    <div class="app-layout">
        <!-- HTML结构部分保持不变，这里省略。请使用上一版中的HTML代码。 -->
        <header class="app-header">
            <div class="logo">
                <i class="fa-solid fa-bars mobile-menu-toggle"></i>
                <span style="margin-left: 15px;">期货量化研究报告</span>
            </div>
            <div class="theme-switcher">
                <i class="fa-solid fa-sun"></i>
            </div>
        </header>

        <aside class="app-sidebar">
            <!--<div class="menu-group open">-->
                <!-- 默认打开第一个 -->
                <!--<div class="menu-title">
                    <span><i class="fa-solid fa-chart-line" style="margin-right: 8px;"></i>螺纹钢</span>
                    <i class="fa-solid fa-chevron-right icon"></i>
                </div>
                <div class="menu-items">
                    <a href="#" class="item active" data-report-id="rb2410-20231231">2023-12-31 报告</a>
                    <a href="#" class="item" data-report-id="rb2410-20231130">2023-11-30 报告</a>
                </div>
            </div>
            <div class="menu-group">
                <div class="menu-title">
                    <span><i class="fa-solid fa-mountain-sun" style="margin-right: 8px;"></i>铁矿石</span>
                    <i class="fa-solid fa-chevron-right icon"></i>
                </div>
                <div class="menu-items">
                    <a href="#" class="item" data-report-id="i2409-20231231">2023-12-31 报告</a>
                </div>
            </div>
            <div class="menu-group">
                <div class="menu-title">
                    <span><i class="fa-solid fa-gas-pump" style="margin-right: 8px;"></i>原油</span>
                    <i class="fa-solid fa-chevron-right icon"></i>
                </div>
                <div class="menu-items">
                    <a href="#" class="item" data-report-id="sc2408-20231231">2023-12-31 报告</a>
                </div>
            </div>-->
        </aside>

        <main class="main-content">
            <div id="report-container">
                <!-- 报告内容将由JS动态注入这里 -->
            </div>
        </main>

        <aside class="table-of-contents">
            <div class="toc-title">报告大纲</div>
            <ul class="toc-list" id="toc-list">
                <!-- 目录将由JS动态生成 -->
            </ul>
        </aside>
    </div>

    <div class="overlay"></div>

    
    <script>
        document.addEventListener('DOMContentLoaded', () => {
            // --- 1. DOM元素获取 ---
            const body = document.body;
            const themeSwitcher = document.querySelector('.theme-switcher');
            const themeIcon = themeSwitcher.querySelector('i');
            const sidebar = document.querySelector('.app-sidebar');
            const mobileMenuToggle = document.querySelector('.mobile-menu-toggle');
            const overlay = document.querySelector('.overlay');
            const reportContainer = document.getElementById('report-container');
            const tocList = document.getElementById('toc-list');
            const sidebarMenuContainer = document.querySelector('.app-sidebar');

            // --- 2. 状态变量 ---
            let chartConfigs = [];
            let renderedChartInstances = [];

            // --- 3. 函数定义 ---

            const renderCharts = () => {
                renderedChartInstances.forEach(chart => chart.dispose());
                renderedChartInstances = [];
                const currentTheme = body.classList.contains('dark-theme') ? 'dark' : 'light';
                chartConfigs.forEach((option, index) => {
                    const chartDom = document.getElementById(`chart-container-${index}`);
                    if (chartDom) {
                        const chart = echarts.init(chartDom, currentTheme, { renderer: 'svg' });
                        chart.setOption(option);
                        renderedChartInstances.push(chart);
                    }
                });
            };

            const applyTheme = (theme) => {
                body.className = theme === 'dark' ? 'dark-theme' : 'light-theme';
                themeIcon.className = theme === 'dark' ? 'fa-solid fa-moon' : 'fa-solid fa-sun';
                renderCharts();
            };

            const generateToc = () => {
                tocList.innerHTML = '';
                const headings = reportContainer.querySelectorAll('h2, h3');
                headings.forEach((h, index) => {
                    const id = `heading-${index}`;
                    h.id = id;
                    const link = document.createElement('a');
                    link.href = `#${id}`;
                    link.textContent = h.textContent.replace(/\([\s\S]*?\)/, '').trim();
                    link.className = h.tagName === 'H3' ? 'toc-h3' : 'toc-h2';
                    const listItem = document.createElement('li');
                    listItem.appendChild(link);
                    tocList.appendChild(listItem);
                });
                document.querySelectorAll('.toc-list a').forEach(anchor => {
                    anchor.addEventListener('click', function (e) {
                        e.preventDefault();
                        const target = document.querySelector(this.getAttribute('href'));
                        if (target) target.scrollIntoView({ behavior: 'smooth' });
                    });
                });
            };

            const tocScrollSpy = () => {
                const headings = Array.from(reportContainer.querySelectorAll('h2, h3'));
                if (headings.length === 0) return;
                const observer = new IntersectionObserver(entries => {
                    entries.forEach(entry => {
                        const id = entry.target.getAttribute('id');
                        const tocLink = document.querySelector(`.toc-list a[href="#${id}"]`);
                        if (tocLink && entry.isIntersecting) {
                            document.querySelectorAll('.toc-list a').forEach(link => link.classList.remove('active'));
                            tocLink.classList.add('active');
                        }
                    });
                }, { rootMargin: "0px 0px -80% 0px" });
                headings.forEach(h => observer.observe(h));
            };

            const parseAndRenderReport = (markdownContent) => {
                reportContainer.innerHTML = '';
                chartConfigs = [];
                let chartIndex = 0;
                const htmlContent = markdownContent.replace(
                    /```json\s*([\s\S]*?)\s*```/g,
                    (match, jsonString) => {
                        try {
                            /*chartConfigs.push(JSON.parse(jsonString));*/
                            console.log("解析得到的json代码块:\r\n" + jsonString);
                            var obj = (new Function(`return ${jsonString}`))();
                            console.log("解析得到的js对象:\r\n" + obj);
                            var c = JSON.parse(jsonString);
                            console.log("解析得到的js对象:\r\n" + c);
                            chartConfigs.push(eval("("+jsonString+")"));
                            return `<div id="chart-container-${chartIndex++}" class="echart-placeholder"></div>`;
                        } catch (e) {
                            return `<div class="error">图表代码解析失败</div>`;
                        }
                    }
                );
                reportContainer.innerHTML = `<div id="report-content">${marked.parse(htmlContent)}</div>`;
                setTimeout(() => {
                    renderCharts();
                    generateToc();
                    tocScrollSpy();
                }, 0);
            };

            const loadReport = async (pzdm, date) => {
                try {
                    reportContainer.innerHTML = '<p>正在加载报告...</p>';
                    const response = await fetch(`api/reports/${pzdm}/${date}`);
                    if (!response.ok) throw new Error(`报告加载失败: ${response.statusText}`);
                    const content = await response.text();
                    parseAndRenderReport(content);
                } catch (error) {
                    console.error('Error loading report:', error);
                    reportContainer.innerHTML = `<p style="color:red;">${error.message}</p>`;
                }
            };

            const createSidebarMenu = (groups) => {
                sidebarMenuContainer.innerHTML = ''; // 清空现有菜单
                groups.forEach((group, groupIndex) => {
                    const groupDiv = document.createElement('div');
                    groupDiv.className = 'menu-group' + (groupIndex === 0 ? ' open' : '');

                    const titleDiv = document.createElement('div');
                    titleDiv.className = 'menu-title';
                    //titleDiv.innerHTML = `
                    //    <span><i class="fa-solid fa-chart-line" style="margin-right: 8px;"></i>${group.name}</span>
                    //    <i class="fa-solid fa-chevron-right icon"></i>`;
                    titleDiv.innerHTML = `
                        <span>${group.name}</span>
                        <i class="fa-solid fa-chevron-right icon"></i>`;
                    titleDiv.addEventListener('click', () => groupDiv.classList.toggle('open'));

                    const itemsDiv = document.createElement('div');
                    itemsDiv.className = 'menu-items';
                    //console.log(group);
                    group.items.forEach(item => {
                        const link = document.createElement('a');
                        link.href = '#';
                        link.className = 'item';
                        link.dataset.pzdm = item.pzdm;
                        link.dataset.date = item.date;
                        link.textContent = `${item.date} 报告`;
                        link.addEventListener('click', (e) => {
                            e.preventDefault();
                            document.querySelectorAll('.app-sidebar .item.active').forEach(i => i.classList.remove('active'));
                            link.classList.add('active');
                            loadReport(item.pzdm, item.date);
                        });
                        itemsDiv.appendChild(link);
                    });

                    groupDiv.appendChild(titleDiv);
                    groupDiv.appendChild(itemsDiv);
                    sidebarMenuContainer.appendChild(groupDiv);
                });

                // 加载第一个报告
                const firstReportLink = sidebarMenuContainer.querySelector('.item');
                if (firstReportLink) {
                    firstReportLink.click();
                } else {
                    reportContainer.innerHTML = '<p>没有可用的报告。</p>';
                }
            };

            const loadSidebarAndInitialReport = async () => {
                try {
                    const response = await fetch('api/reports');
                    if (!response.ok) throw new Error('无法加载菜单');
                    const groups = await response.json();
                    createSidebarMenu(groups);
                } catch (error) {
                    console.error('Error loading sidebar:', error);
                    sidebarMenuContainer.innerHTML = `<p style="color:red; padding: 20px;">${error.message}</p>`;
                }
            };

            // --- 4. 事件监听与初始化 ---
            themeSwitcher.addEventListener('click', () => {
                const newTheme = body.classList.contains('dark-theme') ? 'light' : 'dark';
                localStorage.setItem('theme', newTheme);
                applyTheme(newTheme);
            });
            mobileMenuToggle.addEventListener('click', () => {
                sidebar.classList.toggle('open');
                overlay.classList.toggle('active');
            });
            overlay.addEventListener('click', () => {
                sidebar.classList.remove('open');
                overlay.classList.remove('active');
            });

            // --- 5. 初始加载 ---
            const savedTheme = localStorage.getItem('theme') || (window.matchMedia('(prefers-color-scheme: dark)').matches ? 'dark' : 'light');
            applyTheme(savedTheme); // 先设置主题
            loadSidebarAndInitialReport(); // 再加载数据
        });
    </script>
</body>
</html>